fix(lockfilejobs): ensure using correct file path
authorJyrki Gadinger <nilsding@nilsding.org>
Mon, 2 Jun 2025 13:59:17 +0000 (15:59 +0200)
committerbackportbot[bot] <backportbot[bot]@users.noreply.github.com>
Mon, 2 Jun 2025 14:49:17 +0000 (14:49 +0000)
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
src/libsync/lockfilejobs.cpp
test/testlockfile.cpp

index b5e31e9f2fe97fc17be1070ec59ee8a1e4e9e786..b7506dd4e05b3134c7d85403f3e8cfed73d65e07 100644 (file)
@@ -16,6 +16,7 @@
 
 #include "account.h"
 #include "common/syncjournaldb.h"
+#include "common/syncjournalfilerecord.h"
 #include "filesystem.h"
 
 #include <QLoggingCategory>
@@ -49,7 +50,16 @@ LockFileJob::LockFileJob(const AccountPtr account,
 
 void LockFileJob::start()
 {
-    qCInfo(lcLockFileJob()) << "start with path:" << path()
+    auto remotePath = path();
+
+    SyncJournalFileRecord record;
+    const auto relativePathInDb = path().mid(_remoteSyncPathWithTrailingSlash.size());
+    if (_journal->getFileRecord(relativePathInDb, &record) && record.isValid() && record.isE2eEncrypted()) {
+        remotePath = _remoteSyncPathWithTrailingSlash + record.e2eMangledName();
+        qCDebug(lcLockFileJob).nospace() << "will (un)lock e2ee file path=" << path() << " remotePath=" << remotePath;
+    }
+
+    qCInfo(lcLockFileJob()) << "start with path:" << remotePath
                             << "lock state:" <<  _requestedLockState
                             << "lock owner type:" << _requestedLockOwnerType;
 
@@ -77,7 +87,7 @@ void LockFileJob::start()
         verb = "UNLOCK";
         break;
     }
-    sendRequest(verb, makeDavUrl(path()), request);
+    sendRequest(verb, makeDavUrl(remotePath), request);
 
     AbstractNetworkJob::start();
 }
index 4166de418162b2a3ab8f75b4c14bc919f0aeaba7..64dd29b6073fd40929c8b7f70d3df9d14e3b8a45 100644 (file)
@@ -789,6 +789,83 @@ private slots:
 
         QCOMPARE(lockFileDetectedNewlyUploadedSpy.count(), 1);
     }
+
+    void testLockFile_verifyE2eeFilesUseCorrectPath()
+    {
+        const auto e2eeRoot = QStringLiteral("encrypted");
+        const auto cleartextFilePath = QStringLiteral("encrypted/document.odt");
+        const auto encryptedFilePath = QStringLiteral("encrypted/1e4c70c057994f9daf7bbab71b046d5b");
+
+        FakeFolder fakeFolder{FileInfo{}};
+
+        fakeFolder.localModifier().mkdir(e2eeRoot);
+        fakeFolder.remoteModifier().mkdir(e2eeRoot);
+        fakeFolder.localModifier().insert(cleartextFilePath);
+
+        QVERIFY(fakeFolder.syncOnce());
+
+        // modify local entry for the file to be locked to pretend it's E2E encrypted
+        OCC::SyncJournalFileRecord record;
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(cleartextFilePath, &record));
+        record._e2eEncryptionStatus = OCC::SyncJournalFileRecord::EncryptionStatus::EncryptedMigratedV2_0;
+        record._e2eMangledName = encryptedFilePath.toUtf8();
+        record._path = cleartextFilePath.toUtf8();
+        QVERIFY(fakeFolder.syncJournal().setFileRecord(record));
+
+        // do something similar on the remote -- the encrypted file has a different name
+        fakeFolder.remoteModifier().rename(cleartextFilePath, encryptedFilePath);
+        fakeFolder.remoteModifier().setE2EE(encryptedFilePath, true);
+
+        // another sync run should not fail now, even with our pretended E2Ee setup :-)
+        QVERIFY(fakeFolder.syncOnce());
+
+        auto job = new OCC::LockFileJob(fakeFolder.account(),
+                                        &fakeFolder.syncJournal(),
+                                        QStringLiteral("/") + cleartextFilePath,
+                                        QStringLiteral("/"),
+                                        fakeFolder.localPath(),
+                                        {},
+                                        OCC::SyncFileItem::LockStatus::LockedItem,
+                                        OCC::SyncFileItem::LockOwnerType::UserLock);
+
+        QSignalSpy jobSuccess(job, &OCC::LockFileJob::finishedWithoutError);
+        QSignalSpy jobFailure(job, &OCC::LockFileJob::finishedWithError);
+
+        QString lockRequestPath;
+        connect(fakeFolder.networkAccessManager(), &QNetworkAccessManager::finished, [&lockRequestPath](QNetworkReply *reply) {
+            const auto request = reply->request();
+            if (request.attribute(QNetworkRequest::CustomVerbAttribute).toString() != QStringLiteral("LOCK")) {
+                return;
+            }
+
+            QVERIFY(lockRequestPath.isEmpty());
+            lockRequestPath = request.url().path();
+        });
+
+        job->start();
+
+        QVERIFY(jobSuccess.wait());
+        QCOMPARE(jobFailure.count(), 0);
+
+        // expect the path of the LOCK request to have used the mangled name
+        QVERIFY(!lockRequestPath.isEmpty());
+        QVERIFY(lockRequestPath.contains(encryptedFilePath));
+        QVERIFY(!lockRequestPath.contains(cleartextFilePath));
+
+        auto fileRecord = OCC::SyncJournalFileRecord{};
+        QVERIFY(fakeFolder.syncJournal().getFileRecord(cleartextFilePath, &fileRecord));
+        QVERIFY(fileRecord.isE2eEncrypted());
+        QCOMPARE(fileRecord.e2eMangledName(), encryptedFilePath);
+        QCOMPARE(fileRecord._lockstate._locked, true);
+        QCOMPARE(fileRecord._lockstate._lockEditorApp, QString{});
+        QCOMPARE(fileRecord._lockstate._lockOwnerDisplayName, QStringLiteral("John Doe"));
+        QCOMPARE(fileRecord._lockstate._lockOwnerId, QStringLiteral("admin"));
+        QCOMPARE(fileRecord._lockstate._lockOwnerType, static_cast<qint64>(OCC::SyncFileItem::LockOwnerType::UserLock));
+        QCOMPARE(fileRecord._lockstate._lockTime, 1234560);
+        QCOMPARE(fileRecord._lockstate._lockTimeout, 1800);
+
+        QVERIFY(fakeFolder.syncOnce());
+    }
 };
 
 QTEST_GUILESS_MAIN(TestLockFile)